/**************************************************************************************************
 * light library                                                                                  *
 * Copyright © 2012 David Kretzmer                                                                *
 *                                                                                                *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software  *
 * and associated documentation files (the "Software"), to deal in the Software without           *
 * restriction,including without limitation the rights to use, copy, modify, merge, publish,      *
 * distribute,sublicense, and/or sell copies of the Software, and to permit persons to whom the   *
 * Software is furnished to do so, subject to the following conditions:                           *
 *                                                                                                *
 * The above copyright notice and this permission notice shall be included in all copies or       *
 * substantial portions of the Software.                                                          *
 *                                                                                                *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING  *
 * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND     *
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, *
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.        *
 *                                                                                                *
 *************************************************************************************************/

#pragma once

#include "light/utility/type_traits.hpp"
#include "light/diagnostics/errors.hpp"


namespace light
{
namespace utility
{
	//=============================================================================================
    // Safe conversion of integral types. In case of an arithmetic underflow/overflow throws
	// underflow_error/overflow_error.
	//=============================================================================================
	// Conversion unsigned -> unsigned
	template
	<
		typename TargetType, typename SourceType,
		typename enable_if<is_unsigned<TargetType>::value, int>::type = 0,
		typename enable_if<is_unsigned<SourceType>::value, int>::type = 0
	>
	TargetType safe_int_cast(SourceType src)
	{
		typedef numeric_limits<TargetType> target_limits;
		if(src > target_limits::max())
			throw overflow_error("");

		return static_cast<TargetType>(src);
	}

	// Conversion signed -> unsigned
	template
	<
		typename TargetType, typename SourceType,
		typename enable_if<is_unsigned<TargetType>::value, int>::type = 0,
		typename enable_if<is_signed<SourceType>::value, int>::type = 0
	>
	TargetType safe_int_cast(SourceType src)
	{
		typedef numeric_limits<TargetType> target_limits;
		typedef typename make_unsigned<SourceType>::type src_unsigned_type;

		if(src < 0)
			throw underflow_error("");
		else if(static_cast<src_unsigned_type>(src) > target_limits::max())
			throw overflow_error("");

		return static_cast<TargetType>(src);
	}

	// Conversion unsigned -> signed
	template
	<
		typename TargetType, typename SourceType,
		typename enable_if<is_signed<TargetType>::value, int>::type = 0,
		typename enable_if<is_unsigned<SourceType>::value, int>::type = 0
	>
	TargetType safe_int_cast(SourceType src)
	{
		typedef numeric_limits<TargetType> target_limits;
		typedef typename make_unsigned<TargetType>::type target_unsigned_type;

		if(src > static_cast<target_unsigned_type>(target_limits::max()))
			throw overflow_error("");

		return static_cast<TargetType>(src);
	}

	// Conversion signed -> signed
	template
	<
		typename TargetType, typename SourceType,
		typename enable_if<is_signed<TargetType>::value, int>::type = 0,
		typename enable_if<is_signed<SourceType>::value, int>::type = 0
	>
	TargetType safe_int_cast(SourceType src)
	{
		typedef numeric_limits<TargetType> target_limits;

		if(src > target_limits::max())
			throw overflow_error("");
		else if(src < target_limits::min())
			throw underflow_error("");

		return static_cast<TargetType>(src);
	}

} // namespace: utility

using utility::safe_int_cast;

} // namespace: light

